#include "mainwindow.h"
#include "ui_mainwindow.h"

#include <QMessageBox>




// 禁用内存对齐，确保字段紧凑排列（关键！避免校验和位置偏移）
#pragma pack(push, 1)
// 共用体：数据字段 + 字节数组（包含校验和）
union SerialPacket {
    // 数据主体 + 校验和字段
    struct {
        uint16_t header;
        uint8_t cmdId;         // 指令ID（1字节）
        int32_t voffsetl;         // 核心数据（2字节，支持负数）
        int32_t Vgl;
        int32_t Vbl;
        int32_t HvSet;
        uint8_t checkSum;      // 校验和（1字节，累加取低8位）
        uint8_t tail;
    } data;

    char rawData[sizeof(data)];  // 总长度=1+2+1=4字节（自动匹配结构体大小）
};
#pragma pack(pop)  // 恢复默认内存对齐

SerialPacket packet;

// 禁用内存对齐，确保字段紧凑排列（关键！避免校验和位置偏移）
#pragma pack(push, 1)
// 共用体：数据字段 + 字节数组（包含校验和）
union SerialHvSetPacket {
    // 数据主体 + 校验和字段
    struct {
        uint16_t header;
        uint8_t cmdId;         // 指令ID（1字节）
        int32_t Enable;         // 核心数据（2字节，支持负数）
        uint8_t checkSum;      // 校验和（1字节，累加取低8位）
        uint8_t tail;
    } data;

    char rawData[sizeof(data)];  // 总长度=1+2+1=4字节（自动匹配结构体大小）
};
#pragma pack(pop)  // 恢复默认内存对齐


SerialHvSetPacket HvSet;


void MainWindow::on_refreshButton_clicked()
{
     QStringList serialNamePort;

     /*创建一个串口对象*/
     serialPort = new QSerialPort(this);

    /*搜索所有可用的串口*/
    foreach(const QSerialPortInfo &inf0, QSerialPortInfo::availablePorts()) {
        serialNamePort<<inf0.portName();
    }

    ui->serialBox->addItems(serialNamePort);
}

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    QStringList serialNamePort;

    ui->setupUi(this);
    /* 设置Title*/
    this->setWindowTitle("serial port communication demo v0.0    By.亘芯悦_帅哥强      2025-11-5");

    /*创建一个串口对象*/
    serialPort = new QSerialPort(this);

    /*搜索所有可用的串口*/
    foreach(const QSerialPortInfo &inf0, QSerialPortInfo::availablePorts()) {
        serialNamePort<<inf0.portName();
    }

    ui->serialBox->addItems(serialNamePort);

    /* 按键禁止*/
    ui->closeButton->setEnabled(false);

    ui->VoffsetlSlider->setEnabled(false);
    ui->VoffsetlspinBox->setEnabled(false);

    ui->VglSlider->setEnabled(false);
    ui->VglspinBox->setEnabled(false);

    ui->VblSlider->setEnabled(false);
    ui->VblspinBox->setEnabled(false);

    ui->HvSetSlider->setEnabled(false);
    ui->HvSetspinBox->setEnabled(false);

    ui->HvEnablecheckBox->setEnabled(false);

    ui->baudrateBox->setEnabled(true);
    ui->serialBox->setEnabled(true);

    ui->refreshButton->setEnabled(false);

    /* 定时器相关*/
    QTimer *timer = new QTimer(this);
    connect(timer, SIGNAL(timeout()), this, SLOT(timerUpdate()));
    timer->start(1000);


    packet.data.voffsetl = 50 * 1000;
    packet.data.Vgl = 50 * 1000;
    packet.data.Vbl = 50 * 1000;
    packet.data.HvSet = 0;
}


void MainWindow::on_openButton_clicked()
{
    //串口设置
    serialPort->setPortName(ui->serialBox->currentText());
    serialPort->setBaudRate(ui->baudrateBox->currentText().toInt());
    serialPort->setDataBits(QSerialPort::Data8);
    serialPort->setStopBits(QSerialPort::OneStop);
    serialPort->setParity(QSerialPort::NoParity);

    //打开串口提示框
    if (true == serialPort->open(QIODevice::ReadWrite))
    {
        QMessageBox::information(this, "提示", "串口打开成功");
    }
    else
    {
        QMessageBox::critical(this, "提示", "串口打开失败");

        return;
    }

    /* 使能*/
    ui->openButton->setEnabled(false);
    ui->closeButton->setEnabled(true);


    ui->VoffsetlSlider->setEnabled(true);
    ui->VoffsetlspinBox->setEnabled(true);

    ui->VglSlider->setEnabled(true);
    ui->VglspinBox->setEnabled(true);

    ui->VblSlider->setEnabled(true);
    ui->VblspinBox->setEnabled(true);

    ui->HvSetSlider->setEnabled(true);
    ui->HvSetspinBox->setEnabled(true);

    ui->HvEnablecheckBox->setEnabled(true);

    ui->baudrateBox->setEnabled(false);
    ui->serialBox->setEnabled(false);

    ui->refreshButton->setEnabled(false);

}

void MainWindow::on_closeButton_clicked()
{
    //关闭串口
    serialPort->close();

    /* 禁止*/
    ui->openButton->setEnabled(true);
    ui->closeButton->setEnabled(false);

    ui->VoffsetlSlider->setEnabled(false);
    ui->VoffsetlspinBox->setEnabled(false);

    ui->VglSlider->setEnabled(false);
    ui->VglspinBox->setEnabled(false);

    ui->VblSlider->setEnabled(false);
    ui->VblspinBox->setEnabled(false);

    ui->HvSetSlider->setEnabled(false);
    ui->HvSetspinBox->setEnabled(false);

    ui->HvEnablecheckBox->setEnabled(false);

    ui->baudrateBox->setEnabled(true);
    ui->serialBox->setEnabled(true);

    ui->refreshButton->setEnabled(false);

}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::timerUpdate(void)
{
QDateTime time = QDateTime::currentDateTime();
QString str = time.toString("yyyy-MM-dd hh:mm:ss dddd");
ui->timeLabel->setText(str);
}


void UserSerialSend(void);


void MainWindow::on_VoffsetlSlider_valueChanged(int value)
{
    QString str = QString::number(value);

    ui->VoffsetlspinBox->setValue(value);
}

void MainWindow::on_VoffsetlspinBox_valueChanged(int arg1)
{
    QString str = QString::number(arg1);

    ui->VoffsetlSlider->setValue(arg1);

    packet.data.voffsetl = arg1 * 1000;

    UserSerialSend(arg1);   //调用串口发送数据
}

void MainWindow::on_VglSlider_valueChanged(int value)
{
    QString str = QString::number(value);

    ui->VglspinBox->setValue(value);
}

void MainWindow::on_VglspinBox_valueChanged(int arg1)
{
    QString str = QString::number(arg1);

    ui->VglSlider->setValue(arg1);

    packet.data.Vgl = arg1 * 1000;

    UserSerialSend(arg1);   //调用串口发送数据
}

void MainWindow::on_VblSlider_valueChanged(int value)
{
    QString str = QString::number(value);

    ui->VblspinBox->setValue(value);
}

void MainWindow::on_VblspinBox_valueChanged(int arg1)
{
    QString str = QString::number(arg1);

    ui->VblSlider->setValue(arg1);

    packet.data.Vbl = arg1 * 1000;

    UserSerialSend(arg1);   //调用串口发送数据
}


void MainWindow::on_HvSetSlider_valueChanged(int value)
{
    QString str = QString::number(value);

    ui->HvSetspinBox->setValue(value);
}

void MainWindow::on_HvSetspinBox_valueChanged(int arg1)
{
    QString str = QString::number(arg1);

    ui->HvSetSlider->setValue(arg1);

    packet.data.HvSet = arg1 * 1000;

    UserSerialSend(arg1);   //调用串口发送数据
}


#define CALC_START_NUM (0)  //数组元素[2] 开始计算校验和

/**
 * @brief 计算校验和（数组累加取低8位）
 * @param data 要校验的数据源（不含校验和字段）
 * @param len  要校验的字节长度
 * @return 8位校验和（0~255）
 */
uint8_t calculateCheckSum(const uint8_t* data, uint32_t len) {
    uint16_t sum = 0;  // 用16位避免累加时溢出（8位累加可能提前溢出）
    for (uint32_t i = CALC_START_NUM; i < len; ++i) {
        sum += data[i];  // 逐字节累加
    }
    return static_cast<uint8_t>(sum & 0xFF);  // 取低8位作为校验和
}

void MainWindow::on_HvEnablecheckBox_toggled(bool checked)
{

    //填充个数据主体
    HvSet.data.header = 0x55AA;    //21930
    HvSet.data.cmdId = 0x02;        //命令变成02
    HvSet.data.tail = 0xBB;   //187


    if(checked == true)
    {
        HvSet.data.Enable = 1;
         //serialPort->write("true\n");
    }
    else
    {
        HvSet.data.Enable = 0;
         //serialPort->write("false\n");
    }


    // 2. 计算校验和：仅校验 cmdId + data（共3字节），排除checkSum本身 header  tail
        uint8_t dataBodyLen = sizeof(HvSet.data) - sizeof(HvSet.data.header) - sizeof(HvSet.data.checkSum)  - sizeof(HvSet.data.tail) ;  // 4字节
        HvSet.data.checkSum = calculateCheckSum(
            reinterpret_cast<const uint8_t*>(&HvSet.data.cmdId),  // 数据主体起始地址
            dataBodyLen                                             // 要校验的长度
        );

        // 3. 发送完整数组（含校验和，共4字节）
            qint64 sentLen = serialPort->write(HvSet.rawData,sizeof(HvSet.rawData));

            if (sentLen == sizeof(HvSet.rawData)) {
                QByteArray sendHex = QByteArray(HvSet.rawData, sizeof(HvSet.rawData)).toHex(' ');
                qDebug() << "[发送成功] 数据（十六进制）：" << sendHex
                         << "发送字节数：" << sentLen;
//                qDebug() << "[发送详情] 指令ID：" << static_cast<int>(packet.data.cmdId)
//                         << " 数值：" << packet.data.value
//                         << " 校验和：" << static_cast<int>(packet.data.checkSum);
            } else {
                qDebug() << "[发送失败] 原因：" << serialPort->errorString();
            }
}

/* 声明结构体 数据类型 长度不一样 注意结构体对齐和字节序 */
// Windows（MSVC）：#pragma pack(push, 1)
// Linux/macOS（GCC）：__attribute__((packed))

//#pragma pack(push, 1)
//struct SensorData{
//  qint8 id;
//  qint16 cmd;
//  qint16 u16Data;
//  qint32 u32Data;
//  float fData;
//};
//#pragma pack(pop) // 恢复默认对齐方式

void MainWindow::UserSerialSend(int32_t value)
{
    //填充个数据主体
    packet.data.header = 0x55AA;    //21930
    packet.data.cmdId = 0x01;
    packet.data.tail = 0xBB;   //187


    // 2. 计算校验和：仅校验 cmdId + data（共3字节），排除checkSum本身 header  tail
        uint8_t dataBodyLen = sizeof(packet.data) - sizeof(packet.data.header) - sizeof(packet.data.checkSum)  - sizeof(packet.data.tail) ;  // 4字节
        packet.data.checkSum = calculateCheckSum(
            reinterpret_cast<const uint8_t*>(&packet.data.cmdId),  // 数据主体起始地址
            dataBodyLen                                             // 要校验的长度
        );


        // 3. 发送完整数组（含校验和，共4字节）
            qint64 sentLen = serialPort->write(packet.rawData,sizeof(packet.rawData));

            if (sentLen == sizeof(packet.rawData)) {
                QByteArray sendHex = QByteArray(packet.rawData, sizeof(packet.rawData)).toHex(' ');
                qDebug() << "[发送成功] 数据（十六进制）：" << sendHex
                         << "发送字节数：" << sentLen;
//                qDebug() << "[发送详情] 指令ID：" << static_cast<int>(packet.data.cmdId)
//                         << " 数值：" << packet.data.value
//                         << " 校验和：" << static_cast<int>(packet.data.checkSum);
            } else {
                qDebug() << "[发送失败] 原因：" << serialPort->errorString();
            }




    // //完整示例代码（整合多种数据类型）
//    #pragma pack(push, 1)
//    struct SensorData {
//        quint8 id;
//        float temp;
//        qint16 pressure;
//    };
//    #pragma pack(pop)

//    // 构造发送数据（字符串 + 整数 + 浮点数 + 结构体）
//      QByteArray sendData;
//      QDataStream out(&sendData, QIODevice::WriteOnly);
//      out.setByteOrder(QDataStream::LittleEndian);

//      // 1. 字符串
//      QString str = "SensorData:";
//      out << str;

//      // 2. 整数
//      quint16 deviceId = 0x0001;
//      out << deviceId;

//      // 3. 浮点数
//      float voltage = 3.3f;
//      out << voltage;

//      // 4. 结构体
//      SensorData data = {0x0A, 26.8f, 10120};
//      out.writeRawData(reinterpret_cast<const char*>(&data), sizeof(SensorData));

//      // 发送数据
//      qint64 bytes = serialPort->write(sendData);
//      serialPort->flush();
//      if (bytes != -1) {
//          qDebug() << "发送数据：" << sendData.toHex(' '); // 以十六进制打印发送内容
//          qDebug() << "发送字节数：" << bytes;
//      } else {
//          qDebug() << "发送失败：" << serialPort->errorString();
//      }


//    /* 发送十六进制字节流*/
//    // 方式1：用 QByteArray::fromHex()（字符串转十六进制）
//    QString hexStr = "1234AB";
//    QByteArray hexByteArr = QByteArray::fromHex(hexStr.toUtf8());
//    serialPort->write(hexByteArr); /*串口发送数据 12 34 AB  3个字节*/

//    // 方式2：直接构造字节数组
//    QByteArray hexByteArr2;
//    hexByteArr2.append(0x45);
//    hexByteArr2.append(0x67);
//    hexByteArr2.append(0xCD);
//    qint64 bytesWritten = serialPort->write(hexByteArr2);   //串口发送数据 并返回值。 成功返回发送的字节数 失败返回-1

//    if (bytesWritten == -1) {
//        qDebug() << "发送失败：" << serialPort->errorString();
//    } else {
//        qDebug() << "成功发送" << bytesWritten << "字节";
//    }



//    /* 发送结构体 数据类型 长度不一样 注意结构体对齐和字节序*/
//    SensorData Sensor;

//    Sensor.id = 0xaa;
//    Sensor.cmd = 0x1234;
//    Sensor.u16Data = 100;   //0x0064
//    Sensor.u32Data = 100000;    //0x0001 86A0
//    Sensor.fData = 3.14159f;    //0x4049 0FD0

//    /* 禁用内存对齐 方式 再发送结构体数据
//     * 增加取消对齐指令
//     * #pragma pack(push, 1)
//        struct SensorData{
//          qint8 id;
//          qint16 cmd;
//          qint16 u16Data;
//          qint32 u32Data;
//          float fData;
//        };
//        #pragma pack(pop) // 恢复默认对齐方式

//     * 串口发送一帧13字节 AA 34 12 64 00 A0 86 01 00 D0 0F 49 40
//     * 小端格式 取消了对齐，没有多余额外的数据了。
//    */


//    /*
//     * 内存对齐方式 发送结构体数据。
//     * struct SensorData{
//          qint8 id;
//          qint16 cmd;
//          qint16 u16Data;
//          qint32 u32Data;
//          float fData;
//        };

//     * 串口发送一帧16字节 AA 00 34 12 64 00 FF FF A0 86 01 00 D0 0F 49 40
//     * 小端格式对齐 强制对齐了 AA 00    多了FF FF
//    */

//    QByteArray byteArr;
//    byteArr.resize(sizeof(SensorData));
//    memcpy(byteArr.data(), &Sensor, sizeof(SensorData));
//    serialPort->write(byteArr); /*串口发送数据 */





//    /* 发送结构体 数据类型一致 自定义符合数据 注意结构体对齐和字节序*/

//    /* 声明结构体 */
//    struct SensorData{
//      qint16 id;
//      qint16 cmd;
//      qint16 data;
//    };

//    QByteArray byteArr;

//    SensorData Sensor;

//    Sensor.id = 0x55;
//    Sensor.cmd = 0x01;
//    Sensor.data = 0x2345;

//    // 方式1：memcpy 直接拷贝结构体（推荐）
//    byteArr.resize(sizeof(SensorData));
//    memcpy(byteArr.data(), &Sensor, sizeof(SensorData));
//    serialPort->write(byteArr); /*串口发送一帧 55 00 01 00 45 23*/

//    // 方式2：QDataStream 逐个成员发送（更安全，可控制字节序）
//    QByteArray byteArr2;
//    QDataStream out(&byteArr2, QIODevice::WriteOnly);
//    out.setByteOrder(QDataStream::LittleEndian);

//    out << Sensor.id << Sensor.cmd << Sensor.data;
//    serialPort->write(byteArr2); /*串口发送一帧 55 00 01 00 45 23*/





//    /* 2:memcpy 手动转换（灵活控制） 将整数内存拷贝到 QByteArray，需显式指定字节长度*/
//    QByteArray byteArr;

//    qint32 num = 12345; //0x3039
//    byteArr.resize(sizeof(num)); // 分配 4 字节空间
//    memcpy(byteArr.data(), &num, sizeof(num));  //拷贝内存到字节流
//    serialPort->write(byteArr);  /* 39 30 00 00 */

    // 注意：memcpy 会保留系统原生字节序，跨平台需手动转换字节序！
    // 示例：强制转为大端序（BigEndian）
//    quint16 num16 = 0x1234;
//    quint16 bigEndianNum = qToBigEndian(num16); // Qt 字节序转换函数
//    byteArr.resize(sizeof(bigEndianNum));
//    memcpy(byteArr.data(), &bigEndianNum, sizeof(bigEndianNum));

//    serialPort->write(byteArr);




    /* 1:QDataStream（简单通用，推荐）自动处理字节序和类型转换 */
//    QByteArray byteArr;
//    QDataStream out(&byteArr, QIODevice::WriteOnly);
//    out.setByteOrder(QDataStream::LittleEndian);

//    out << value; //发送函数参数
//    serialPort->write(byteArr); //发送上面所有的out数据 一帧多个字节


    // 发送 浮点型数据
    // 发送 float（4 字节）
//    float fVal = 3.14159f;   //
//    out << fVal;

//    // 发送 double（8 字节）
//    double dVal = 123.4567890123;
//    out << dVal;

//    serialPort->write(byteArr); //发送上面所有的out数据 一帧多个字节

    /*实际发送数据 00 00 00 00    FA 21 09 40    0B 40 FB 07 3C DD 5E 40*/

    /*  多发了4个00   低位补0  按照结构体多发了4个字节 自动补齐了？
     * 按照8字节发送了
     * 如果改为先8字节， 后4字节呢？ 也还是补了4个00
     * Double 数据对的。 float 3.14159 对应 16进制也不对啊 网络计算为0x40490FD0
     * 单发float 数据试一试 4byte 也是8个字节。
     * 3.14 		变成 3.1400001   	0x40091EB860
        3.14159f	变成 3.14159012  	0x400921FA
    */


//    // 发送整型数据   一帧数据
//    // 发送 16 位无符号整数（quint16，2 字节）
//    quint16 num16 = 0x1234;
//    out << num16;

//    // 发送 32 位有符号整数（qint32，4 字节）
//    qint32 num32 = -123456;
//    out << num32;

//    // 发送 64 位整数（quint64，8 字节）
//    quint64 num64 = 1234567890123;
//    out << num64;

//    serialPort->write(byteArr); //发送上面所有的out数据 一帧多个字节

//    /*实际发送数据 34 12    C0 1D FE FF    CB 04 FB 71 1F 01 00 00*/

}


//void MainWindow::on_onButton_clicked()
//{
//    //串口发送字符串
//    serialPort->write("ON\n");
//    //打印调试信息
//    qDebug("ON\n");
//}

//void MainWindow::on_offButton_clicked()
//{
//    //串口发送字符串
//    serialPort->write("OFF\n");
//    //打印调试信息
//    qDebug("OFF\n");
//}

void MainWindow::on_HvEnablecheckBox_clicked(bool checked)
{

}

void MainWindow::on_HvEnablecheckBox_stateChanged(int arg1)
{

}




